﻿using System;
using System.Linq.Expressions;
using System.Runtime.Serialization;

namespace ExpressionSerialization
{
  [DataContract]
  [KnownType(typeof(XBinaryExpression))]
  [KnownType(typeof(XConditionalExpression))]
  [KnownType(typeof(XExpressionList))]
  [KnownType(typeof(XInvocationExpression))]
  [KnownType(typeof(XLambdaExpression))]
  [KnownType(typeof(XMemberExpression))]
  [KnownType(typeof(XMethodCallExpression))]
  [KnownType(typeof(XNewArrayExpression))]
  [KnownType(typeof(XNewExpression))]
  [KnownType(typeof(XParameterExpression))]
  [KnownType(typeof(XParameterExpressionList))]
  [KnownType(typeof(XTypeBinaryExpression))]
  [KnownType(typeof(XUnaryExpression))]
  [KnownType(typeof(XParameterExpression))]
  public abstract class XExpression
  {
    #region -- Static ------------------
		public static XExpression CreateFromExpression(Expression source_)
    {
      if(source_ == null) return null;
      XExpression res;
      if(source_ is BinaryExpression) {
        res = new XBinaryExpression();
      } else if(source_ is MethodCallExpression) {
        res = new XMethodCallExpression();
      } else if(source_ is ConstantExpression) {
        res = new XConstantExpression();
      } else if(source_ is MemberExpression) {
        res = new XMemberExpression();
      } else if(source_ is ParameterExpression) {
        res = new XParameterExpression();
      } else if(source_ is ConditionalExpression) {
        res = new XConditionalExpression();
      } else if(source_ is UnaryExpression) {
        res = new XUnaryExpression();
      } else if(source_ is TypeBinaryExpression) {
        res = new XTypeBinaryExpression();
      } else if(source_ is NewArrayExpression) {
        res = new XNewArrayExpression();
      } else if(source_ is InvocationExpression) {
        res = new XInvocationExpression();
      } else if(source_ is LambdaExpression) {
        res = new XLambdaExpression();
      } else if(source_ is NewExpression) {
        //res = XExpression.ToCreateXConstantExpression(source_ as NewExpression);
        res = new XNewExpression();
      } else {
        // toto neznám = chyba
        throw new Exception("Unsupported type " + source_.GetType().AssemblyQualifiedName);
      }
      res.FromExpression(source_);
      // test - převádí lokální proměnné na konstanty
      if(res.NodeType == ExpressionType.MemberAccess) {
        //todo:Tady by to chtělo nějak inteligentně, toto je jediný případ, který jsem testoval
        if(res is XMemberExpression && (res as XMemberExpression).Expression is XConstantExpression) {
          res = (res as XMemberExpression).Expression;
        } else {
          //throw new Exception("serializace - toto neumím - "+res.TypeName);
        }
      }
      return res;
    }
    //---------------------------
    public static XConstantExpression ToCreateXConstantExpression(NewExpression source_)
    {
      //source_.Constructor.Invoke(source_.Arguments);
      var res = new XConstantExpression();
      res.NodeType = ExpressionType.Constant;
      res.TypeName = typeof(DateTime).ToSerializableForm();
      res.Value = new DateTime(2007,7,1);
      return res;
    }


  	#endregion

    #region -- Ctor --------------------------
    public XExpression() { }
    
    #endregion
    #region -- Fields -----------------------
    //internal ExpressionType _nodeType;

    #endregion
  
    #region -- Property ---------------------
    //------
    [DataMember]
    public string TypeName { get; set; }
    [DataMember]
    public ExpressionType NodeType { get; set; }

    public Type GetExpressionType(Type dataContract_)
    {
      return ExpressionSerialization.ReflectionExtensions.TypeFromSerializableForm(TypeName, dataContract_); 
    } 

    #endregion


    public virtual void FromExpression(Expression source_)
    {
      TypeName = source_.Type.ToSerializableForm();
      NodeType = source_.NodeType;//.ToString();
    }

    public virtual Expression ToExpression(Type dataContract_, System.Collections.Generic.List<ParameterExpression> parameters_ )
    {
      return null;
    }
  }
}
